/**
 * Description: TFTP 简单文件传输协议客户端
 * File: tftp.c |Time: 2021-04-13 07:23
 */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>

#define ERR(msg) do {   \
    perror(msg);        \
    printf("at:%s:%s:%d\n", __FILE__, __func__, __LINE__); \
    exit(EXIT_FAILURE); \
}while(0)

#define BUF_SIZE 516

int download(const char *ip, const char *port, const char *filename);
int upload();

int main(int argc, char *argv[])
{
    if (argc < 4) {
        printf("Usage: %s <ip> <port> <filename>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    download(argv[1], argv[2], argv[3]);

    return 0;
}

int download(const char *ip, const char *port, const char *filename)
{
    // 发起下载请求
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    if (sockfd == -1) {
        ERR("socket");
    }

    struct sockaddr_in serveraddr;
    socklen_t addrlen = sizeof(serveraddr);
    char buf[BUF_SIZE] = {0};
    int recvlen = 0;

    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons((unsigned short)atoi(port));
    serveraddr.sin_addr.s_addr = inet_addr(ip);

    *(unsigned short *)buf = htons(1);
    sprintf(buf+2, "%s%c%s%c", filename, 0, "octet", 0);

    // 这里，连接请求发送成功后， TFTP服务器会另开一个端口进行通信
    if (sendto(sockfd, buf, sizeof(buf), 0,
            (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)
    {
        ERR("sendto");
    }

    unsigned short opcode;
    unsigned short packid;
    char * data_p;

    while(1) {
        // 接收数据
        bzero(buf, BUF_SIZE);
        recvlen = recvfrom(sockfd, buf, sizeof(buf), 0, 
                (struct sockaddr *)&serveraddr, &addrlen);
        if (recvlen == -1) {
            ERR("recvfrom");
        }

        
        opcode = ntohs(*(unsigned short *)buf);
        packid = ntohs(*(unsigned short *)(buf + 2));
        data_p = buf + 4;

        if (opcode == 5) {
            printf("opcode:%d packid:%d data:%s\n", opcode, packid, data_p);
            exit(EXIT_FAILURE);
        } else {
            printf("opcode %d packid:%d res=%d.\n", opcode, packid, recvlen);
        }

        // 发送ack确认信息 ack 操作码为 4
        bzero(buf, BUF_SIZE);
        *(unsigned short *)buf = htons(4);
        *(unsigned short *)(buf + 2) = htons(packid);
        
        if (sendto(sockfd, buf, 4, 0,
                (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)
        {
            ERR("sendto");
        }
       
        if (recvlen < BUF_SIZE) {
            printf("download finished. res=%d\n", recvlen);
            break;
        }
    }
    close(sockfd);
    return EXIT_SUCCESS;
}

int upload()
{
    return 0;
}

